﻿package net.wangit.framework.util;

import net.wangit.MapList;
import net.wangit.context.ActionContext;
import net.wangit.context.ActionContextHelper;
import net.wangit.framework.config.DebugSettings;
import net.wangit.framework.expression.ConstantExpression;
import net.wangit.framework.expression.NullableSQLExpression;
import net.wangit.framework.expression.OrgExpression;
import net.wangit.framework.expression.Request2SessionExpression;
import net.wangit.framework.expression.RequestExpression;
import net.wangit.framework.expression.SQLExpression;
import net.wangit.framework.expression.SessionExpression;
import net.wangit.framework.expression.UserExpression;
import net.wangit.framework.expression.VariableExpression;
import net.wangit.framework.session.AgileSession;
import net.wangit.framework.session.UnitSettings;
import net.wangit.jdbc.DB;
import net.wangit.jdbc.DBFactory;
import net.wangit.jdbc.DBImplementor;
import net.wangit.jdbc.JDBCException;
import net.wangit.jdbc.QueryParameters;
import net.wangit.support.SqlProvider;
import net.wangit.util.Checker;
import net.wangit.util.ClassUtil;
import net.wangit.view.unit.UnitComponent;
import net.wangit.view.unit.UnitFactory;
import net.wangit.view.unit.component.ListUnit;
import net.wangit.view.unit.util.UnitUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class UnitDataUtil {

	private static Log log = LogFactory
			.getLog(net.wangit.framework.util.UnitDataUtil.class);

	public UnitDataUtil() {
	}

	public static void loadDataFromDatabase(ActionContext ac, UnitComponent uc)
			throws Exception {
		DB db = DBFactory.getDB(uc.getDatasource());
		String sql = getSql(ac, uc, db);
		if (DebugSettings.isDebugEnabled(log, uc))
			log.debug((new StringBuilder(String.valueOf(UnitUtil.getUrl(uc))))
					.append(": load data from sql: ").append(sql).toString());
		try {
			QueryParameters qp = getQueryParameters(ac, db, sql,
					uc.getDomain(), uc.getId());
			uc.setData(db.query(sql, qp));
		} catch (Exception e) {
			log.warn((new StringBuilder(String.valueOf(UnitUtil.getUrl(uc))))
					.append(" could not load data: ").append(sql).toString());
			e.printStackTrace();
		}
	}

	public static String getSql(ActionContext ac, UnitComponent uc, DB db)
			throws Exception {
		String sql = getOriginalSql(ac, uc);
		String queryUnitId = uc.getQueryUnitId();
		String tableName = uc.getTableName();
		if (!Checker.isEmpty(queryUnitId) && !Checker.isEmpty(tableName))
			sql = QueryProcessor.addConditionAndOrder(ac, (DBImplementor) db,
					sql, uc.getDomain(), queryUnitId, tableName, null);
		if (uc.hasDataPrivilege())
			sql = DataPrivilegeUtil.getUnitSql(uc.getDomain(), uc.getId(), sql);
		sql = resolveSQL(ac, sql);
		return sql;
	}

	public static QueryParameters getQueryParameters(ActionContext ac, DB db,
			String sql, String domain, String listUnitId) throws JDBCException {
		QueryParameters qp = new QueryParameters();
		UnitComponent listUnit = UnitFactory.getUnitWithoutClone(domain,
				listUnitId);
		if ((listUnit instanceof ListUnit) && listUnit.getListSize() > 0) {
			AgileSession session = ActionContextHelper.getSession(ac);
			UnitSettings us = session.getUnitSettings(domain, listUnitId);
			int listSize = listUnit.getListSize();
			int maxSize = count(db, sql);
			us.setMaxSize(maxSize);
			int maxPageNumber = getMaxPageNumber(listSize, maxSize);
			int pageNumber = getPageNumber(ac, listUnitId, us, maxPageNumber);
			if (maxSize > 0) {
				qp.setFirstRow(listSize * (pageNumber - 1));
				qp.setMaxRows(listSize);
			}
		}
		return qp;
	}

	public static String resolveSQL(ActionContext ac, String sql) {
		sql = SQLExpression.resolve(ac, sql);
		sql = NullableSQLExpression.resolve(ac, sql);
		sql = ConstantExpression.resolve(sql);
		sql = VariableExpression.resolve(sql);
		sql = UserExpression.resolve(ac, sql);
		sql = OrgExpression.resolve(ac, sql);
		sql = RequestExpression.resolve(ac, sql);
		sql = SessionExpression.resolve(ac, sql);
		sql = Request2SessionExpression.resolve(ac, sql);
		return sql;
	}

	private static int getMaxPageNumber(int listSize, int maxSize) {
		return (int) Math.ceil(((double) maxSize * 1.0D) / (double) listSize);
	}

	private static int getPageNumber(ActionContext ac, String listUnitId,
			UnitSettings us, int maxPageNumber) {
		if (maxPageNumber == 0) {
			us.setPageNumber(0);
			return 0;
		}
		String pageNumberStr = ac.getRequestParameter((new StringBuilder(String
				.valueOf(listUnitId))).append(".p").toString());
		int pageNumber = 0;
		try {
			pageNumber = Integer.parseInt(pageNumberStr);
		} catch (Exception exception) {
		}
		if (pageNumber <= 0)
			pageNumber = us.getPageNumber();
		if (pageNumber <= 0 || pageNumber > maxPageNumber)
			pageNumber = 1;
		us.setPageNumber(pageNumber);
		return pageNumber;
	}

	private static int count(DB db, String sql) throws JDBCException {
		int index = sql.indexOf(" order ");
		if (index > 0) {
			String tail = sql.substring(index + 7);
			int indexOfBy = tail.indexOf("by");
			if (indexOfBy >= 0
					&& tail.substring(0, indexOfBy).trim().equals(""))
				sql = sql.substring(0, index);
		}
		sql = ((DBImplementor) db).getJDBCSettings().getDatabase().getCountSql(
				sql);
		MapList list = db.query(sql);
		return Integer.parseInt(list.getRow(0).get(0));
	}

	private static String getOriginalSql(ActionContext ac, UnitComponent uc)
			throws Exception {
		String sql = uc.getDataExpression();
		if (uc.getDataMode() == 0)
			try {
				SqlProvider provider = (SqlProvider) ClassUtil.loadClass(sql)
						.newInstance();
				sql = provider.getSql(ac);
			} catch (Exception e) {
				log.error((new StringBuilder("Unit SqlProvider error : "))
						.append(uc.getDomain()).append("/").append(uc.getId())
						.append("(").append(sql).append(")").toString());
				throw e;
			}
		return sql;
	}

}
